#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
配置管理模块
支持从多种源加载配置：环境变量、JSON文件、YAML文件等
"""

import os
import json
import logging
from typing import Dict, Any, Optional, Union, List
from pathlib import Path

try:
    import yaml
    YAML_AVAILABLE = True
except ImportError:
    YAML_AVAILABLE = False

logger = logging.getLogger(__name__)

class ConfigManager:
    """配置管理器"""
    
    def __init__(self, config_file: Optional[str] = None, env_prefix: str = "MONITOR_"):
        """
        初始化配置管理器
        
        Args:
            config_file: 配置文件路径
            env_prefix: 环境变量前缀
        """
        self.config_file = config_file
        self.env_prefix = env_prefix
        self._config = {}
        self._default_config = self._get_default_config()
        
        # 加载配置
        self._load_config()
    
    def _get_default_config(self) -> Dict[str, Any]:
        """获取默认配置"""
        return {
            # 邮件配置
            'email': {
                'enabled': False,
                'smtp_server': 'smtp.gmail.com',
                'smtp_port': 587,
                'use_tls': True,
                'username': '',
                'password': '',
                'sender': '',
                'recipients': []
            },
            
            # 监控配置
            'monitoring': {
                'server_monitor': {
                    'enabled': True,
                    'check_interval': 300,  # 5分钟
                    'cpu_threshold': 80.0,
                    'memory_threshold': 85.0,
                    'disk_threshold': 90.0,
                    'network_threshold': 100.0,  # MB/s
                    'process_count_threshold': 500
                },
                'memory_monitor': {
                    'enabled': True,
                    'check_interval': 30,  # 30秒
                    'warning_threshold': 75.0,
                    'critical_threshold': 85.0,
                    'auto_cleanup': True
                }
            },
            
            # 报告配置
            'reporting': {
                'enabled': True,
                'daily_report_time': '09:00',
                'weekly_report_day': 'monday',
                'include_charts': False,
                'max_history_days': 30
            },
            
            # 数据库配置（可选）
            'database': {
                'enabled': False,
                'type': 'sqlite',
                'host': 'localhost',
                'port': 3306,
                'name': 'monitor.db',
                'username': '',
                'password': '',
                'table_prefix': 'monitor_'
            },
            
            # 日志配置
            'logging': {
                'level': 'INFO',
                'file': 'logs/monitor.log',
                'max_size_mb': 10,
                'backup_count': 5,
                'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
            },
            
            # Web界面配置
            'web': {
                'enabled': False,
                'host': '0.0.0.0',
                'port': 8080,
                'debug': False
            },
            
            # 其他配置
            'general': {
                'timezone': 'Asia/Shanghai',
                'data_retention_days': 30,
                'alert_cooldown_minutes': 60
            }
        }
    
    def _load_config(self):
        """加载配置"""
        # 1. 从默认配置开始
        self._config = self._default_config.copy()
        
        # 2. 从配置文件加载
        if self.config_file and os.path.exists(self.config_file):
            file_config = self._load_config_file(self.config_file)
            if file_config:
                self._merge_config(self._config, file_config)
        
        # 3. 从环境变量加载
        env_config = self._load_env_config()
        if env_config:
            self._merge_config(self._config, env_config)
        
        # 4. 验证配置
        self._validate_config()
    
    def _load_config_file(self, file_path: str) -> Optional[Dict[str, Any]]:
        """从文件加载配置"""
        try:
            file_path = Path(file_path)
            
            if not file_path.exists():
                logger.warning(f"配置文件不存在: {file_path}")
                return None
            
            with open(file_path, 'r', encoding='utf-8') as f:
                if file_path.suffix.lower() in ['.yml', '.yaml']:
                    if not YAML_AVAILABLE:
                        logger.error("YAML配置文件需要安装PyYAML: pip install pyyaml")
                        return None
                    return yaml.safe_load(f)
                elif file_path.suffix.lower() == '.json':
                    return json.load(f)
                else:
                    logger.error(f"不支持的配置文件格式: {file_path.suffix}")
                    return None
        
        except Exception as e:
            logger.error(f"加载配置文件失败 {file_path}: {e}")
            return None
    
    def _load_env_config(self) -> Dict[str, Any]:
        """从环境变量加载配置"""
        env_config = {}
        
        # 定义环境变量映射
        env_mappings = {
            # 邮件配置
            f'{self.env_prefix}EMAIL_ENABLED': ('email', 'enabled', bool),
            f'{self.env_prefix}SMTP_SERVER': ('email', 'smtp_server', str),
            f'{self.env_prefix}SMTP_PORT': ('email', 'smtp_port', int),
            f'{self.env_prefix}SMTP_USE_TLS': ('email', 'use_tls', bool),
            f'{self.env_prefix}SMTP_USERNAME': ('email', 'username', str),
            f'{self.env_prefix}SMTP_PASSWORD': ('email', 'password', str),
            f'{self.env_prefix}EMAIL_SENDER': ('email', 'sender', str),
            f'{self.env_prefix}EMAIL_RECIPIENTS': ('email', 'recipients', list),
            
            # 监控配置
            f'{self.env_prefix}SERVER_CHECK_INTERVAL': ('monitoring', 'server_monitor', 'check_interval', int),
            f'{self.env_prefix}CPU_THRESHOLD': ('monitoring', 'server_monitor', 'cpu_threshold', float),
            f'{self.env_prefix}MEMORY_THRESHOLD': ('monitoring', 'server_monitor', 'memory_threshold', float),
            f'{self.env_prefix}DISK_THRESHOLD': ('monitoring', 'server_monitor', 'disk_threshold', float),
            
            f'{self.env_prefix}MEMORY_CHECK_INTERVAL': ('monitoring', 'memory_monitor', 'check_interval', int),
            f'{self.env_prefix}MEMORY_WARNING_THRESHOLD': ('monitoring', 'memory_monitor', 'warning_threshold', float),
            f'{self.env_prefix}MEMORY_CRITICAL_THRESHOLD': ('monitoring', 'memory_monitor', 'critical_threshold', float),
            
            # 日志配置
            f'{self.env_prefix}LOG_LEVEL': ('logging', 'level', str),
            f'{self.env_prefix}LOG_FILE': ('logging', 'file', str),
            
            # Web配置
            f'{self.env_prefix}WEB_ENABLED': ('web', 'enabled', bool),
            f'{self.env_prefix}WEB_HOST': ('web', 'host', str),
            f'{self.env_prefix}WEB_PORT': ('web', 'port', int),
            
            # 数据库配置
            f'{self.env_prefix}DB_ENABLED': ('database', 'enabled', bool),
            f'{self.env_prefix}DB_TYPE': ('database', 'type', str),
            f'{self.env_prefix}DB_HOST': ('database', 'host', str),
            f'{self.env_prefix}DB_PORT': ('database', 'port', int),
            f'{self.env_prefix}DB_NAME': ('database', 'name', str),
            f'{self.env_prefix}DB_USERNAME': ('database', 'username', str),
            f'{self.env_prefix}DB_PASSWORD': ('database', 'password', str),
        }
        
        for env_var, mapping in env_mappings.items():
            if len(mapping) == 4:
                section, subsection, key, value_type = mapping
                value = os.getenv(env_var)
                if value is not None:
                    converted_value = self._convert_env_value(value, value_type)
                    if converted_value is not None:
                        if section not in env_config:
                            env_config[section] = {}
                        if subsection not in env_config[section]:
                            env_config[section][subsection] = {}
                        env_config[section][subsection][key] = converted_value
            else:
                section, key, value_type = mapping
                value = os.getenv(env_var)
                if value is not None:
                    converted_value = self._convert_env_value(value, value_type)
                    if converted_value is not None:
                        if section not in env_config:
                            env_config[section] = {}
                        env_config[section][key] = converted_value
        
        return env_config
    
    def _convert_env_value(self, value: str, value_type: type) -> Any:
        """转换环境变量值类型"""
        try:
            if value_type == bool:
                return value.lower() in ('true', '1', 'yes', 'on')
            elif value_type == int:
                return int(value)
            elif value_type == float:
                return float(value)
            elif value_type == list:
                # 支持逗号分隔的列表
                return [item.strip() for item in value.split(',') if item.strip()]
            else:
                return value
        except (ValueError, TypeError) as e:
            logger.warning(f"环境变量值转换失败 {value} -> {value_type}: {e}")
            return None
    
    def _merge_config(self, base_config: Dict[str, Any], new_config: Dict[str, Any]):
        """合并配置"""
        for key, value in new_config.items():
            if key in base_config and isinstance(base_config[key], dict) and isinstance(value, dict):
                self._merge_config(base_config[key], value)
            else:
                base_config[key] = value
    
    def _validate_config(self):
        """验证配置"""
        errors = []
        
        # 验证邮件配置
        if self._config['email']['enabled']:
            email_config = self._config['email']
            if not email_config.get('smtp_server'):
                errors.append("邮件功能已启用但未配置SMTP服务器")
            if not email_config.get('sender'):
                errors.append("邮件功能已启用但未配置发件人")
            if not email_config.get('recipients'):
                errors.append("邮件功能已启用但未配置收件人")
        
        # 验证监控阈值
        server_config = self._config['monitoring']['server_monitor']
        if server_config['cpu_threshold'] <= 0 or server_config['cpu_threshold'] > 100:
            errors.append("CPU阈值必须在0-100之间")
        if server_config['memory_threshold'] <= 0 or server_config['memory_threshold'] > 100:
            errors.append("内存阈值必须在0-100之间")
        if server_config['disk_threshold'] <= 0 or server_config['disk_threshold'] > 100:
            errors.append("磁盘阈值必须在0-100之间")
        
        memory_config = self._config['monitoring']['memory_monitor']
        if memory_config['warning_threshold'] >= memory_config['critical_threshold']:
            errors.append("内存警告阈值必须小于严重阈值")
        
        # 验证Web配置
        if self._config['web']['enabled']:
            web_config = self._config['web']
            if not (1 <= web_config['port'] <= 65535):
                errors.append("Web端口必须在1-65535之间")
        
        if errors:
            error_msg = "配置验证失败:\n" + "\n".join(f"- {error}" for error in errors)
            logger.error(error_msg)
            raise ValueError(error_msg)
    
    def get(self, key: str, default: Any = None) -> Any:
        """
        获取配置值，支持点号分隔的嵌套键
        
        Args:
            key: 配置键，支持 'section.subsection.key' 格式
            default: 默认值
        
        Returns:
            配置值
        """
        keys = key.split('.')
        value = self._config
        
        try:
            for k in keys:
                value = value[k]
            return value
        except (KeyError, TypeError):
            return default
    
    def set(self, key: str, value: Any):
        """
        设置配置值
        
        Args:
            key: 配置键，支持 'section.subsection.key' 格式
            value: 配置值
        """
        keys = key.split('.')
        config = self._config
        
        # 导航到最后一级的父级
        for k in keys[:-1]:
            if k not in config:
                config[k] = {}
            config = config[k]
        
        # 设置值
        config[keys[-1]] = value
    
    def get_section(self, section: str) -> Dict[str, Any]:
        """
        获取配置段
        
        Args:
            section: 配置段名称
        
        Returns:
            配置段字典
        """
        return self._config.get(section, {})
    
    def get_email_config(self) -> Dict[str, Any]:
        """获取邮件配置"""
        return self.get_section('email')
    
    def get_monitoring_config(self) -> Dict[str, Any]:
        """获取监控配置"""
        return self.get_section('monitoring')
    
    def get_server_monitor_config(self) -> Dict[str, Any]:
        """获取服务器监控配置"""
        return self.get('monitoring.server_monitor', {})
    
    def get_memory_monitor_config(self) -> Dict[str, Any]:
        """获取内存监控配置"""
        return self.get('monitoring.memory_monitor', {})
    
    def get_logging_config(self) -> Dict[str, Any]:
        """获取日志配置"""
        return self.get_section('logging')
    
    def get_web_config(self) -> Dict[str, Any]:
        """获取Web配置"""
        return self.get_section('web')
    
    def get_database_config(self) -> Dict[str, Any]:
        """获取数据库配置"""
        return self.get_section('database')
    
    def is_email_enabled(self) -> bool:
        """检查邮件功能是否启用"""
        return self.get('email.enabled', False)
    
    def is_web_enabled(self) -> bool:
        """检查Web界面是否启用"""
        return self.get('web.enabled', False)
    
    def is_database_enabled(self) -> bool:
        """检查数据库是否启用"""
        return self.get('database.enabled', False)
    
    def save_config(self, file_path: Optional[str] = None) -> bool:
        """
        保存配置到文件
        
        Args:
            file_path: 保存路径，默认使用初始化时的配置文件路径
        
        Returns:
            是否保存成功
        """
        target_file = file_path or self.config_file
        if not target_file:
            logger.error("未指定配置文件路径")
            return False
        
        try:
            target_path = Path(target_file)
            
            # 确保目录存在
            target_path.parent.mkdir(parents=True, exist_ok=True)
            
            with open(target_path, 'w', encoding='utf-8') as f:
                if target_path.suffix.lower() in ['.yml', '.yaml']:
                    if not YAML_AVAILABLE:
                        logger.error("保存YAML配置文件需要安装PyYAML")
                        return False
                    yaml.dump(self._config, f, default_flow_style=False, 
                             allow_unicode=True, indent=2)
                else:
                    json.dump(self._config, f, ensure_ascii=False, indent=2)
            
            logger.info(f"配置已保存到: {target_path}")
            return True
        
        except Exception as e:
            logger.error(f"保存配置文件失败: {e}")
            return False
    
    def reload_config(self):
        """重新加载配置"""
        self._load_config()
        logger.info("配置已重新加载")
    
    def to_dict(self) -> Dict[str, Any]:
        """返回完整配置字典"""
        return self._config.copy()
    
    def __str__(self) -> str:
        """字符串表示"""
        return f"ConfigManager(config_file={self.config_file}, sections={list(self._config.keys())})"

def create_sample_config(file_path: str, format_type: str = 'json') -> bool:
    """
    创建示例配置文件
    
    Args:
        file_path: 配置文件路径
        format_type: 格式类型 ('json' 或 'yaml')
    
    Returns:
        是否创建成功
    """
    try:
        config_manager = ConfigManager()
        sample_config = config_manager._default_config
        
        file_path = Path(file_path)
        file_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(file_path, 'w', encoding='utf-8') as f:
            if format_type.lower() == 'yaml':
                if not YAML_AVAILABLE:
                    logger.error("创建YAML配置文件需要安装PyYAML")
                    return False
                yaml.dump(sample_config, f, default_flow_style=False, 
                         allow_unicode=True, indent=2)
            else:
                json.dump(sample_config, f, ensure_ascii=False, indent=2)
        
        logger.info(f"示例配置文件已创建: {file_path}")
        return True
    
    except Exception as e:
        logger.error(f"创建示例配置文件失败: {e}")
        return False